/**
 * 表单验证插件
 * Author: 陆楚良
 * Version: 2.0.4
 * Date: 2016/09/07
 * QQ: 874449204
 *
 * https://git.oschina.net/luchg/jquery.CL_Valitator.js.git
 *
 * License: http://www.apache.org/licenses/LICENSE-2.0
 */
!function(undefined){
    function valitator($){
        "use strict";
        function Queue(){
            var list  = [];
            var canExecute = true;
            var timer = null;
            var self = this;
            self.queue = function(callback){
                list.push(callback);
                canExecute && self.dequeue();
                return self;
            };
            self.dequeue = function(){
                var self=self,s = list.shift();
                canExecute = false;
                if(s){
                    clearTimeout(timer);
                    timer = setTimeout(function(){
                        $.type(s)=="function" && s.call(self);
                    },10);
                }else{
                    canExecute = true;
                }
                return self;
            };
            self.clear = function(){
                clearTimeout(timer);
                list = [];
                canExecute = true;
                return self;
            };
        }

        var data_config = "cl_valitator_config";
        var data_config_id = "cl_valitator_config_id";
        // 以字节为单位计算字符串长度
        var lengthAt = function(strTemp, len){
                var i, sum = 0;
                len = isNum(len) ? len : 2;
                for (i = 0; i < strTemp.length; i++) {
                    if ((strTemp.charCodeAt(i) >= 0) && (strTemp.charCodeAt(i) <= 255))
                        sum = sum + 1;
                    else
                        sum = sum + len;
                }
                return sum
            };

        var RegExps={
                "require"   : /\S+/,    // 不为空
                "email"     : /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,      // 邮箱
                "url"       : /^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?$/,   // 链接地址
                "currency"  : /^(0|[1-9][0-9]*)(\.[0-9]{1,2})?$/,   // 货币
                "number"    : /^\d+$/,                              // 数字
                "zip"       : /^\d{6}$/,                            // 邮编
                "integer"   : /^(0|-?[1-9][0-9]*)$/,                // 整数
                "pinteger"  : /^(0|[1-9][0-9]*)$/,                  // 正整数
                "double"    : /^-?(0|[1-9][0-9]*)\.[0-9]+$/,        // 浮点数
                "pdouble"   : /^(0|[1-9][0-9]*)\.[0-9]+$/,          // 正浮点数
                "english"   : /^[A-Za-z]+$/,                        // 英文字母
                "chinese"   : /^[\u4e00-\u9fa5]+$/                  // 汉字
            };

        var isObj = function(e){return $.type(e)=="object"};
        var isArr = function(e){return $.type(e)=="array" };
        var isStr = function(e){return $.type(e)=="string"};
        var isNum = function(e){return $.type(e)=="number"};
        var isFun = function(e){return $.type(e)=="function"};
        var isReg = function(e){return $.type(e)=="regexp"};

        // 取唯一ID
        var newID = function(id){
            return function(){return id++}
        }(0);
        // 用于支持使用函数设置error、success、net_error、focus等
        var parseMsg = function(s, self){
            return isFun(s) ? s.call(self) : s;
        };


        $.fn.CL_Valitator = function(c, r){
            if(isFun(c)){
                // 当第一个参数为函数时候认为是触发验证操作
                this.each(function(){
                    var C = $(this).data(data_config);
                    var err=false;
                    $.each(C._func, function(id,v){
                        if(!err){
                            v.focus.call(v.self);
                            v.blur.call(v.self, c);
                            if(C._dict[id]._lock===false){
                                err = true;
                            }
                        }
                    });
                });
            }else if(isFun(r)){
                // 当第二个参数为函数时候认为是触发第一个参数(选择器)选中的表单的验证
                this.each(function(){
                    var C = $(this).data(data_config);
                    $(this).find(c).each(function(){
                        var id = $(this).data(data_config_id);
                        if(C._func[id]){
                            C._func[id].focus.call(this);
                            C._func[id].blur.call(this, undefined, r);
                        }
                    });
                });
            }else if(isStr(r)){
                // 当第二个参数为字符串时候认为是自定义标记第一个参数(选择器)选中的表单的验证为错误的
                // 而这个参数的字符串内容即为自定义的错误信息
                this.each(function(){
                    var top = this;
                    var C   = $(this).data(data_config);
                    $(this).find(c).each(function(){
                        var self = this;
                        var id   = $(this).data(data_config_id);
                        var R    = C._dict[id];
                        if(R){
                            R._lock = true;
                            isFun(R.onlock)  && R.onlock.call(self);        // 触发表单锁定事件
                            isFun(C.onlock)  && C.onlock.call(top);         // 触发全局锁定事件
                            isFun(R.onerror) && R.onerror.call(self, r);    // 触发表单错误事件
                            isFun(C.onerror) && C.onerror.call(self, r);    // 触发顶级错误事件
                        }
                    });
                });
            }else if(r===undefined){
                // 大设置
                c = $.extend({
                    onerror     : null,         // 可选，错误时触发，接收到错误信息参数，this为引发错误的表单
                    onsuccess   : null,         // 可选，正确时触发，接收到正确信息参数，this为引发错误的表单，单个表单一次验证不管有多少条规则，最终它只触发一次
                    onlock      : null,         // 可选，锁定时触发，this为选择器选择的DOM
                    onunlock    : null,         // 可选，解锁时触发，this为选择器选择的DOM
                    onfocus     : null,         // 可选，获得焦点时触发，this为获得焦点的表单
                }, isObj(c) ? c : {} );
                // 避免被用户配置串改私有设置
                delete c._lock;
                delete c._dict;
                delete c._func;
                this.each(function(){
                    var $this = $(this), config = $.extend({_lock:false, _dict:{}, _func:{}}, $this.data(data_config)||{}, c);
                    $this.data(data_config, config);    // 存TOP设置
                    isObj(config.rules) && $.each(config.rules, function(k,v){
                        $this.CL_Valitator(k,v);        // 遍历存表单设置
                    });
                });

            }else{
                this.each(function(){
                    // 添加、修改规则
                    var top = this;
                    var $top = $(top);

                    isObj($top.data(data_config)) || $top.CL_Valitator({});
                    var C = $top.data(data_config);

                    $top.find(c).each(function(){
                        var self = this;
                        var $self = $(self);
                        var indexOf = $.inArray(self, C._dict);
                        var conf;
                        var ID = $self.data(data_config_id);    // ID用作唯一标识
                        switch($.type(r)){
                            case "array":
                                conf = isNum(ID)?C._dict[ID]:{};
                                conf.rules = r;
                                break;
                            case "object":
                                conf = $.extend(isNum(ID)?C._dict[ID]:{}, r);
                                conf.rules = isArr(conf.rules) ? conf.rules : [];
                                break;
                            case "null":
                                conf = {rules:[],_lock:false};
                                break;
                        }
                        if(conf){
                            if(!isNum(ID)){
                                var ID2 = newID();
                                $self.data(data_config_id, ID2);
                                C._dict[ID2] = conf;
                            }else{
                                C._dict[ID] = conf;
                            }

                            // 解析规则
                            var rules=conf.rules,f = [],i;      // f用于存放验证规则处理函数
                            /*
                                原理：
                                    解析验证规则，封装为一个函数，存储到f列表中，再在表单获得焦点时标记锁定，触发全局锁定事件，
                                    然后在表单失去焦点时，将f列表添加到队列依次执行，如果验证失败则结束验证，并触发顶级错误事件，
                                    如果验证成功则继续下一条验证规则，如果全部验证成功，表示表单输入正确，标志解锁，进行全部表单遍历，
                                    如果全部已解锁，则触发顶级成功事件与解锁事件
                             */

                            var success   = conf.success    || null,
                                error     = conf.error  || null;
                            for(i=0;i<rules.length;i++){    // 遍历规则
                                switch(rules[i].type.toLowerCase()){    // 使用效率较高的switch解析规则
                                    case "regexp":
                                        // 正则验证
                                        f[f.length] = (function(r){     // 采用闭包让变量命名更自由
                                            var a = isStr(r.action) ? RegExps[r.action.toLowerCase()] : r.action,   // 读取验证操作，如果是字符串，尝试读取内置正则
                                                m = r.match===false ? false : true;     // 读取匹配结果验证，如果为true，则匹配通过为正确，反之如果为false，则匹配不通过为正确
                                            return function(callback){
                                                    // 如果正则有效并且匹配通过，回调成功，否则回调失败
                                                    (isReg(a) && a.test($self.val())==m) ? callback(r, true, r.success||success) : callback(r, false, r.error||error);
                                                };
                                        })(rules[i]);
                                        break;

                                    case "length":
                                        // 长度验证
                                        f[f.length] = (function(r){
                                            var a = r.action;   // 读取验证操作
                                            return function(callback){
                                                    var l = r.lengthAt?lengthAt($self.val(),r.lengthAt):$self.val().length;    // 计算长度，如果设置以字节为单位，则以字节计算长度
                                                    (l>=a[0] && l<=a[1]) ? callback(r, true, r.success||success) : callback(r, false, r.error||error);
                                                };
                                        })(rules[i]);
                                        break;

                                    case "minlength":
                                        // 最小长度验证
                                        f[f.length] = (function(r){
                                            return function(callback){
                                                    var l = r.lengthAt?lengthAt($self.val(),r.lengthAt):$self.val().length;
                                                    (l>=r.action) ? callback(r, true, r.success||success) : callback(r, false, r.error||error);
                                                };
                                        })(rules[i]);
                                        break;

                                    case "maxlength":
                                        // 最大长度验证
                                        f[f.length] = (function(r){
                                            return function(callback){
                                                    var l = r.lengthAt?lengthAt($self.val(),r.lengthAt):$self.val().length;
                                                    (l<=r.action) ? callback(r, true, r.success||success) : callback(r, false, r.error||error);
                                                };
                                        })(rules[i]);
                                        break;

                                    case "callback":
                                        // 函数验证
                                        f[f.length] = (function(r){
                                            return function(callback){
                                                    r.action.call(self, $self.val(), function(status,msg){  // 执行用户配置的回调函数，并给其传递回调解析
                                                        // 解析用户回调
                                                        status ? callback(r, true, msg||(r.success||success)) : callback(r, false, msg||(r.error||error));
                                                    });
                                                };
                                        })(rules[i]);
                                        break;

                                    case "ajax":
                                        // ajax验证
                                        f[f.length] = (function(r){
                                            var data = $.param(r.data||{});     // 解析存储额外数据（避免因引用导致参数发生变化）
                                            return function(callback){
                                                    isFun(r.onbefore) && r.onbefore.call(self);     // 触发请求前事件
                                                    $.ajax({            // ajax请求开始
                                                        url: r.action,
                                                        data: data+"&"+(r.name||$self.attr("name"))+"="+encodeURIComponent($self.val()),    // 拼接请求数据，额外数据+&字段名=值
                                                        type: r.method||'get',  // 缺省get请求
                                                        error: function(){
                                                            callback(r, false, r.net_error);    // 网络错误回调
                                                        },
                                                        success:function(data){
                                                            if(isFun(r.oncomplete)){    // 如果用户配置了自定义数据解析，则使用用户自定义的解析回调
                                                                r.oncomplete.call(self, data, function(status,msg){
                                                                    // 解析用户回调的数据
                                                                    status ? callback(r, true, msg||(r.success||success)) : callback(r, false, msg||(r.error||error))
                                                                });
                                                            }
                                                            else{
                                                                // 如果用户没有配置自定义数据解析，则使用默认的，如果为true，则认为正确，否则错误，不区分大小写
                                                                $.trim(data).toUpperCase()=="TRUE" ? callback(r, true, r.success||success) : callback(r, false, r.error||error);
                                                            }
                                                        }
                                                    });
                                                };
                                        })(rules[i]);
                                        break;
                                }
                            }
                            conf._f = f;

                            // 事件绑定
                            if(!isNum(ID)){
                                var Q = new Queue();        // 创建队列
                                C._func[ID2] = {
                                    self:self,
                                    focus:function(){       // 获得焦点事件
                                        var C = $top.data(data_config);     // 读取全局设置，（以防中途修改，因此需要在事件里读取）
                                        if(isObj(C)){
                                            var R = C._dict[$self.data(data_config_id)];    // 读取当前规则，（以防中途修改，因此需要在事件里读取）
                                            if(R){
                                                var focus = parseMsg(R.focus, self);// 解析焦点信息
                                                Q.clear();                          // 清空队列，避免用户频繁操作引发的一系列问题
                                                R._lock = true;                     // 锁定当前表单
                                                isFun(R.onlock) && R.onlock.call(self);     // 触发表单锁定
                                                isFun(C.onlock) && C.onlock.call(top);      // 触发全局锁定
                                                isFun(R.onfocus)&& R.onfocus.call(self, focus);    // 触发表单焦点事件
                                                isFun(C.onfocus)&& C.onfocus.call(self, focus);    // 触发全局焦点事件
                                            }
                                        }
                                    },
                                    // allCallback用于触发全部验证， selfCallback用于触发自身验证
                                    blur:function(allCallback, selfCallback){
                                        var C = $top.data(data_config);     // 存储全局设置，（以防中途修改，因此需要在事件里读取）
                                        if(isObj(C)){
                                            var R = C._dict[$self.data(data_config_id)];    // 存储当前规则，（以防中途修改，因此需要在事件里读取）
                                            if(R){
                                                Q.clear();          // 清除队列，避免用户频繁操作引发的一系列问题
                                                var success_msg = null;         // 存储成功信息（多条条件的成功信息，缺省只使用配置的最后一条）
                                                var unlock = function(){
                                                    if(isFun(C.onunlock) || isFun(allCallback)){
                                                        var p=true;
                                                        $.each(C._dict, function(k,v){
                                                            if(v._lock!==false){
                                                                p=false;
                                                            }
                                                        });
                                                        if(p){
                                                            isFun(C.onunlock) && C.onunlock.call(top);
                                                            isFun(allCallback) && allCallback.call(top);
                                                        }
                                                    }
                                                };
                                                if(R._f.length==0){
                                                    R._lock = false;                                            // 解锁当前表单
                                                    isFun(R.onunlock)  && R.onunlock.call(self);                // 触发表单解锁事件
                                                    isFun(selfCallback)&& selfCallback.call(self);
                                                    unlock();   // 解析所有表单是否全部解锁
                                                    isFun(R.onsuccess) && R.onsuccess.call(self, success_msg);  // 触发表单成功事件
                                                    isFun(C.onsuccess) && C.onsuccess.call(self, success_msg);  // 触发顶级成功事件
                                                }else{
                                                    for(var i=0; i<R._f.length; i++){   // 遍历验证方法
                                                        !function(i){                   // 使用闭包解决i在回调下引发的问题
                                                            Q.queue(function(){         // 添加队列
                                                                R._f[i](function(r, status, msg){   // 执行验证
                                                                    var _msg = parseMsg(msg, self);
                                                                    // C为顶部配置， R为表单配置， r为当前规则配置
                                                                    if(status){     // 验证成功
                                                                        success_msg = msg||success_msg; // 成功信息赋值
                                                                        var _success_msg = parseMsg(success_msg, self);
                                                                        if(i==R._f.length-1){                                           // 如果是最后一条验证方法
                                                                            R._lock = false;                                            // 解锁当前表单
                                                                            isFun(R.onunlock) && R.onunlock.call(self);                 // 触发表单解锁事件
                                                                            isFun(selfCallback)&& selfCallback.call(self);
                                                                            unlock();   // 解析所有表单是否全部解锁
                                                                            isFun(r.onsuccess) && r.onsuccess.call(self, _msg);          // 触发验证规则的成功事件
                                                                            isFun(R.onsuccess) && R.onsuccess.call(self, _success_msg);  // 触发表单成功事件
                                                                            isFun(C.onsuccess) && C.onsuccess.call(self, _success_msg);  // 触发顶级成功事件
                                                                        }else{
                                                                            isFun(r.onsuccess) && r.onsuccess.call(self, _msg);          // 触发验证规则的成功事件
                                                                        }
                                                                        Q.dequeue();    // 释放队列
                                                                    }else{
                                                                        Q.clear();      // 如果失败，清空队列
                                                                        isFun(R.onlock)  && R.onlock.call(self);            // 触发表单锁定事件
                                                                        isFun(C.onlock)  && C.onlock.call(top);             // 触发全局锁定事件
                                                                        isFun(r.onerror) && r.onerror.call(self, _msg);      // 触发验证规则的错误事件
                                                                        isFun(R.onerror) && R.onerror.call(self, _msg);      // 触发表单错误事件
                                                                        isFun(C.onerror) && C.onerror.call(self, _msg);      // 触发顶级错误事件
                                                                    }
                                                                });
                                                            });
                                                        }(i);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                };
                                $self.focus(function(){C._func[ID2].focus()}).blur(function(){C._func[ID2].blur()});
                            }
                            f.length==0 && C._func[isNum(ID) ? ID : ID2].blur();    // 当重写清空验证时候，触发验证使得全局解锁事件可以及时触发
                        }
                    });
                });
            }

            return this;
        };

        return $;
    }


    // RequireJS && SeaJS && GlightJS
    if (typeof define === "function") {
        define(function() {
            return valitator;
        });
    // NodeJS
    } else if (typeof exports !== "undefined") {
        module.exports = valitator;
    } else {
        valitator(this.jQuery);
    }
}();
